import json
import threading
import tkinter as tk
from tkinter import scrolledtext, messagebox, ttk, filedialog
from selenium import webdriver
from selenium.webdriver.edge.options import Options
import requests
import datetime
import tempfile
import re
import pandas as pd
import ttkbootstrap as tb

WRITE_TO_DB = False  # 是否写入数据库，True为写入，False为不写入

class QCCApp:
    def __init__(self, root):
        # root = tb.Window(themename='cosmo')  # 启动时用
        # 其它控件全部用tb.xxx代替ttk.xxx
        # 例如
        self.root = root
        self.root.title("融利昌快速查档系统v1.1")
        self.root.geometry('900x700')
        self.driver = None
        self.cookies = None
        self.cookie_timer = None
        self.df = None  # 新增：保存导入的DataFrame

        # 使用ttk美化
        style = ttk.Style()
        style.theme_use('clam')

        # 标题（去掉）
        # title_label = ttk.Label(
        #     root, text="融利昌快速查档系统v1.1", font=("微软雅黑", 20, "bold")
        # )
        # title_label.pack(pady=(20, 10), anchor='center')

        # 主体Frame，横向分为输入区和按钮区
        top_frame = ttk.Frame(root)
        top_frame.pack(pady=10, padx=20, fill='x')

        # 身份证输入区
        input_frame = ttk.Frame(top_frame)
        input_frame.pack(side='left', padx=(0, 20))
        ttk.Label(
            input_frame, text="身份证号:", font=("微软雅黑", 12)
        ).grid(row=0, column=0, padx=5, pady=5, sticky='e')
        self.entry_id = tb.Entry(input_frame, width=20, font=("微软雅黑", 12))
        self.entry_id.grid(row=0, column=1, padx=5, pady=5)

        # 按钮区
        btn_frame = ttk.Frame(top_frame)
        btn_frame.pack(side='left', padx=10)
        self.btn_login = tb.Button(
            btn_frame, text="登录企查查", command=self.open_qcc, bootstyle='primary'
        )
        self.btn_login.grid(row=0, column=0, padx=8, pady=5)
        self.btn_query = ttk.Button(
            btn_frame, text="查询", command=self.query_api, state=tk.DISABLED
        )
        self.btn_query.grid(row=0, column=1, padx=8, pady=5)
        self.btn_reset = ttk.Button(
            btn_frame, text="重置", command=self.reset_fields
        )
        self.btn_reset.grid(row=0, column=2, padx=8, pady=5)
        # 登录状态放在查询按钮旁边
        self.login_status_label = tk.Label(
            btn_frame, text="未登录", font=("微软雅黑", 11), fg="red"
        )
        self.login_status_label.grid(row=0, column=3, padx=(15, 0), pady=5)

        # 导入导出按钮单独一行
        file_btn_frame = ttk.Frame(root)
        file_btn_frame.pack(pady=(0, 5), padx=20, fill='x')
        self.btn_import = ttk.Button(
            file_btn_frame, text="导入Excel", command=self.import_excel
        )
        self.btn_import.pack(side='left', padx=8, pady=5)
        self.btn_export = ttk.Button(
            file_btn_frame, text="导出Excel", command=self.export_excel
        )
        self.btn_export.pack(side='left', padx=8, pady=5)
        # 新增：初始禁用导入导出按钮
        self.btn_import.config(state=tk.DISABLED)
        self.btn_export.config(state=tk.DISABLED)

        # 结果区
        result_frame = ttk.LabelFrame(root, text="结果", padding=(10, 8))
        result_frame.pack(padx=20, pady=(5, 0), fill='both', expand=True)
        self.text_result = scrolledtext.ScrolledText(
            result_frame, width=100, height=6, font=("Consolas", 11)
        )
        self.text_result.pack(fill='both', expand=True)

        # 控制台区
        console_frame = ttk.LabelFrame(root, text="爬虫结果", padding=(10, 8))
        console_frame.pack(padx=20, pady=(10, 20), fill='both', expand=True)
        self.text_console = scrolledtext.ScrolledText(
            console_frame, width=100, height=4, font=("Consolas", 10)
        )
        self.text_console.pack(fill='both', expand=True)

        # 直接写死网址
        self.QCC_URL = 'https://www.qcc.com/'

        # 主窗口自适应，窗口调小
        root.update_idletasks()
        root.minsize(700, 480)
        root.geometry('800x520')
        root.rowconfigure(2, weight=1)
        root.rowconfigure(3, weight=1)
        root.columnconfigure(0, weight=1)

    def open_qcc(self):

        def _open():
            # 弹框在主线程创建
            def show_wait():
                self.wait_win = tk.Toplevel(self.root)
                self.wait_win.title("请稍候")
                self.wait_win.geometry("250x80+500+300")
                ttk.Label(
                    self.wait_win,
                    text="正在启动浏览器，请稍候...",
                    font=("微软雅黑", 12)
                ).pack(expand=True, pady=20)
                self.wait_win.update()
            self.root.after(0, show_wait)

            edge_options = Options()
            edge_options.add_experimental_option("detach", True)
            tmp_dir = tempfile.mkdtemp()
            edge_options.add_argument(f'--user-data-dir={tmp_dir}')
            self.driver = webdriver.Edge(options=edge_options)
            self.driver.get(self.QCC_URL)
            # 浏览器启动后，关闭弹框
            def close_wait():
                if hasattr(self, 'wait_win') and self.wait_win.winfo_exists():
                    self.wait_win.destroy()
            self.root.after(0, close_wait)
            # 重启定时器
            if self.cookie_timer:
                self.root.after_cancel(self.cookie_timer)
                self.cookie_timer = None
            self.btn_query.config(state=tk.DISABLED)
            self.start_cookie_timer()
        threading.Thread(target=_open).start()

    def start_cookie_timer(self):
        def check_cookie():
            if self.driver:
                try:
                    cookies = self.driver.get_cookies()
                    if cookies:
                        cookie_dict = {c['name']: c['value'] for c in cookies}
                        # 用cookie试探请求一次接口
                        headers = {
                            'accept': 'application/json, text/plain, */*',
                            'accept-language': 'zh-CN,zh;q=0.9',
                            'content-type': 'application/json',
                            'origin': 'https://www.qcc.com',
                            'referer': (
                                'https://www.qcc.com/web/bigsearch/judicial-litigation/search'
                            ),
                            'user-agent': (
                                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                                'AppleWebKit/537.36 (KHTML, like Gecko) '
                                'Chrome/137.0.0.0 Safari/537.36'
                            ),
                        }
                        url = 'https://www.qcc.com/api/judicial-litigation/mergeSearch'
                        data = {
                            "searchKey": "test",
                            "pageSize": 1,
                            "pageIndex": 1,
                            "mode": 0,
                            "isRiskBigData": True,
                            "isShowEm": True,
                            "isAgg": True,
                            "viewType": "card"
                        }
                        try:
                            resp = requests.post(
                                url, headers=headers, cookies=cookie_dict,
                                data=json.dumps(data), timeout=5
                            )
                            if resp.status_code == 200 and 'data' in resp.json():
                                self.cookies = cookie_dict
                                self.btn_query.config(state=tk.NORMAL)
                                self.btn_import.config(state=tk.NORMAL)  # 新增
                                self.btn_export.config(state=tk.NORMAL)  # 新增
                                self.login_status_label.config(
                                    text="登录成功", fg="green"
                                )
                                return
                        except Exception:
                            pass
                except Exception:
                    pass
            self.login_status_label.config(text="未登录", fg="red")
            self.btn_query.config(state=tk.DISABLED)  # 新增
            self.btn_import.config(state=tk.DISABLED)  # 新增
            self.btn_export.config(state=tk.DISABLED)  # 新增
            self.cookie_timer = self.root.after(1000, check_cookie)
        check_cookie()

    def query_api(self):
        if not self.cookies:
            messagebox.showerror("错误", "请先登录企查查！")
            return
        search_key = self.entry_id.get().strip()
        if not search_key:
            messagebox.showerror("错误", "请输入身份证号！")
            return
        headers = {
            'accept': 'application/json, text/plain, */*',
            'accept-language': 'zh-CN,zh;q=0.9',
            'content-type': 'application/json',
            'origin': 'https://www.qcc.com',
            'referer': (
                'https://www.qcc.com/web/bigsearch/judicial-litigation/search'
            ),
            'user-agent': (
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                'AppleWebKit/537.36 (KHTML, like Gecko) '
                'Chrome/137.0.0.0 Safari/537.36'
            ),
        }
        url = 'https://www.qcc.com/api/judicial-litigation/mergeSearch'
        data = {
            "searchKey": search_key,
            "pageSize": 40,
            "pageIndex": 1,
            "mode": 0,
            "isRiskBigData": True,
            "isShowEm": True,
            "isAgg": True,
            "viewType": "card"
        }

        def do_request():
            try:
                resp = requests.post(
                    url, headers=headers, cookies=self.cookies, data=json.dumps(data)
                )
                self.text_console.delete(1.0, tk.END)
                self.text_console.insert(tk.END, resp.text)
                # 处理结果
                try:
                    resp_json = resp.json()
                    add_logs = []
                    if 'data' in resp_json and isinstance(resp_json['data'], list):
                        for item in resp_json['data']:
                            an_no = item.get('AnNo', '')
                            type_desc = item.get('TypeDesc', '')
                            # 被执行人可能是列表
                            name = ''
                            if isinstance(item.get('Name'), list) and item['Name']:
                                name = item['Name'][0].get('Name', '')
                            else:
                                name = item.get('Name', '')
                            id_no = item.get('IDNo', '')
                            # 仅当包含<em>标签时才去除
                            if '<em>' in id_no:
                                id_no = re.sub(r'<.*?>', '', id_no)
                            # 立案日期转换
                            li_an_date = item.get('LiAnDate', '')
                            if (isinstance(li_an_date, int) or
                                (isinstance(li_an_date, str) and 
                                 li_an_date.isdigit())):
                                try:
                                    li_an_date_fmt = datetime.datetime.fromtimestamp(
                                        int(li_an_date)
                                    ).strftime('%Y-%m-%d')
                                except Exception:
                                    li_an_date_fmt = str(li_an_date)
                            else:
                                li_an_date_fmt = str(li_an_date)
                            court_name = item.get('CourtName', '')
                            biao_di = item.get('BiaoDi', '')
                            write_to_db = WRITE_TO_DB
                            write_result = ''
                            if write_to_db:
                                write_result = self.add_case_to_db(
                                    search_key, an_no, type_desc, name, id_no, 
                                    li_an_date_fmt, court_name, biao_di
                                )
                                add_logs.append(
                                    self.format_case_result(
                                        an_no, type_desc, name, id_no, li_an_date_fmt, 
                                        court_name, biao_di, write_result
                                    )
                                )
                            else:
                                add_logs.append(
                                    self.format_case_result(
                                        an_no, type_desc, name, id_no, li_an_date_fmt, court_name, biao_di
                                    )
                                )
                        self.text_result.delete(1.0, tk.END)
                        self.text_result.insert(tk.END, '\n'.join(add_logs))
                    else:
                        self.text_result.delete(1.0, tk.END)
                        self.text_result.insert(tk.END, '无数据')
                except Exception as e:
                    self.text_result.delete(1.0, tk.END)
                    self.text_result.insert(tk.END, f"结果解析失败: {e}")
            except Exception as e:
                self.text_console.delete(1.0, tk.END)
                self.text_console.insert(
                    tk.END, f"请求失败: {e}"
                )
        threading.Thread(target=do_request).start()

    def reset_fields(self):
        self.entry_id.delete(0, tk.END)
        self.text_result.delete(1.0, tk.END)
        self.text_console.delete(1.0, tk.END)

    def add_case_to_db(self, search_key, an_no, type_desc, name, id_no, li_an_date_fmt, court_name, biao_di):
        """写库接口调用，返回写库结果字符串"""
        add_url = 'https://testwebapi.thefuturepay.com/api/CaseData/AddCaseData'
        add_payload = {
            "tenancyCode": "XSJWY",
            "queryWords": search_key,
            "caseNo": an_no,
            "caseType": type_desc,
            "caseMan": name,
            "caseManId": id_no,
            "caseDate": li_an_date_fmt,
            "exeCourt": court_name,
            "exeTargetNum": biao_di,
            "createdBy": "",
            "remark": ""
        }
        try:
            add_resp = requests.post(add_url, json=add_payload, timeout=10)
            add_json = add_resp.json()
            if add_json.get('status') is True:
                return "写库结果：成功"
            else:
                tip = add_json.get('data', {}).get('tip', '')
                return f"写库结果：失败，原因：{tip or add_json.get('message', '')}"
        except Exception as e:
            return f"写库结果：失败，原因：{e}"

    def format_case_result(self, an_no, type_desc, name, id_no, li_an_date_fmt, court_name, biao_di, write_result=None):
        """格式化案件信息，write_result可选"""
        base = (
            f"案件号：{an_no}\n类型：{type_desc}\n被执行人：{name}\n证件号/组织机构代码：{id_no}\n立案日期：{li_an_date_fmt}\n执行法院：{court_name}\n执行标的(元)：{biao_di}"
        )
        if write_result:
            return f"{base}\n{write_result}\n{'-'*60}"
        else:
            return f"{base}\n{'-'*60}"

    def import_excel(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("Excel files", "*.xlsx;*.xls")],
            title="选择Excel文件"
        )
        if not file_path:
            return
        try:
            df = pd.read_excel(file_path, dtype=str)
            if 'contactsCardId' not in df.columns or 'cstName' not in df.columns:
                messagebox.showerror("错误", "Excel中必须包含'contactsCardId'和'cstName'列")
                return
            self.text_result.delete(1.0, tk.END)
            self.text_console.delete(1.0, tk.END)
            # 新增：准备data列
            if 'data' not in df.columns:
                df['data'] = ''
            # 只处理从第6行开始的数据
            for idx, row in df.iloc[4:].iterrows():
                card_id = str(row['contactsCardId']).strip()
                name = str(row['cstName']).strip()
                if not card_id or not name:
                    continue
                self.text_console.insert(tk.END, f'正在查询：{name}（{card_id}）...\n')
                self.root.update()
                result = self.query_and_filter(card_id, name)
                self.text_result.insert(tk.END, result + '\n')
                # 新增：写入data列
                df.at[idx, 'data'] = result
            self.df = df  # 保存到self.df
            messagebox.showinfo("完成", "Excel批量查询完成！")
        except Exception as e:
            messagebox.showerror(
                "错误", f"导入或查询失败：{e}"
            )

    def export_excel(self):
        if self.df is None:
            messagebox.showerror("错误", "请先导入并查询Excel！")
            return
        file_path = filedialog.asksaveasfilename(
            defaultextension='.xlsx',
            filetypes=[("Excel files", "*.xlsx")],
            title="导出Excel文件"
        )
        if not file_path:
            return
        try:
            self.df.to_excel(file_path, index=False)
            messagebox.showinfo("导出成功", f"文件已保存到：{file_path}")
        except Exception as e:
            messagebox.showerror("导出失败", f"导出Excel失败：{e}")

    def query_and_filter(self, card_id, name):
        headers = {
            'accept': 'application/json, text/plain, */*',
            'accept-language': 'zh-CN,zh;q=0.9',
            'content-type': 'application/json',
            'origin': 'https://www.qcc.com',
            'referer': (
                'https://www.qcc.com/web/bigsearch/judicial-litigation/search'
            ),
            'user-agent': (
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
                'AppleWebKit/537.36 (KHTML, like Gecko) '
                'Chrome/137.0.0.0 Safari/537.36'
            ),
        }
        url = 'https://www.qcc.com/api/judicial-litigation/mergeSearch'
        data = {
            "searchKey": card_id,
            "pageSize": 40,
            "pageIndex": 1,
            "mode": 0,
            "isRiskBigData": True,
            "isShowEm": True,
            "isAgg": True,
            "viewType": "card"
        }
        try:
            resp = requests.post(
                url, headers=headers, cookies=self.cookies, data=json.dumps(data)
            )
            resp_json = resp.json()
            add_logs = []
            if 'data' in resp_json and isinstance(resp_json['data'], list):
                for item in resp_json['data']:
                    # 姓名过滤
                    item_name = ''
                    if isinstance(item.get('Name'), list) and item['Name']:
                        item_name = item['Name'][0].get('Name', '')
                    else:
                        item_name = item.get('Name', '')
                    if item_name != name:
                        continue
                    an_no = item.get('AnNo', '')
                    type_desc = item.get('TypeDesc', '')
                    id_no = item.get('IDNo', '')
                    if '<em>' in id_no:
                        id_no = re.sub(r'<.*?>', '', id_no)
                    li_an_date = item.get('LiAnDate', '')
                    if (
                        isinstance(li_an_date, int)
                        or (isinstance(li_an_date, str) and li_an_date.isdigit())
                    ):
                        try:
                            li_an_date_fmt = datetime.datetime.fromtimestamp(
                                int(li_an_date)
                            ).strftime('%Y-%m-%d')
                        except Exception:
                            li_an_date_fmt = str(li_an_date)
                    else:
                        li_an_date_fmt = str(li_an_date)
                    court_name = item.get('CourtName', '')
                    biao_di = item.get('BiaoDi', '')
                    add_logs.append(
                        self.format_case_result(
                            an_no, type_desc, item_name, id_no, li_an_date_fmt, court_name, biao_di
                        )
                    )
            if not add_logs:
                return f"{name}（{card_id}）：无匹配案件\n{'-'*60}"
            return '\n'.join(add_logs)
        except Exception as e:
            return (
                f"{name}（{card_id}）：查询失败，原因：{e}\n{'-'*60}"
            )

if __name__ == "__main__":
    root = tk.Tk()
    app = QCCApp(root)
    root.mainloop()
